]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
injectionManager: Support overriding vfuncs
authorFlorian Müllner <fmuellner@gnome.org>
Sat, 22 Jul 2023 16:44:08 +0000 (18:44 +0200)
committerFlorian Müllner <fmuellner@gnome.org>
Sat, 5 Aug 2023 10:35:30 +0000 (12:35 +0200)
Overriding vfuncs can be useful, in particular when we convert
to ES modules, and exported symbols cannot easily be swapped out.

Adapt overrideMethod() to work correctly in that case as well.

Part-of: <https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2809>

js/extensions/extension.js
tests/unit/injectionManager.js

index a0d3f049dd55689939db8d3574c5a82a42689439..c6a05f08bc8d68f0244fc3e9a5c9799dbd1ac8b1 100644 (file)
@@ -1,3 +1,5 @@
+const Gi = imports._gi;
+
 import {ExtensionBase, GettextWrapper} from './sharedInternals.js';
 
 const {extensionManager} = imports.ui.main;
@@ -48,7 +50,7 @@ export class InjectionManager {
      */
     overrideMethod(prototype, methodName, createOverrideFunc) {
         const originalMethod = this._saveMethod(prototype, methodName);
-        prototype[methodName] = createOverrideFunc(originalMethod);
+        this._installMethod(prototype, methodName, createOverrideFunc(originalMethod));
     }
 
     /**
@@ -66,7 +68,7 @@ export class InjectionManager {
         if (originalMethod === undefined)
             delete prototype[methodName];
         else
-            prototype[methodName] = originalMethod;
+            this._installMethod(prototype, methodName, originalMethod);
 
         savedProtoMethods.delete(methodName);
         if (savedProtoMethods.size === 0)
@@ -96,4 +98,13 @@ export class InjectionManager {
         savedProtoMethods.set(methodName, originalMethod);
         return originalMethod;
     }
+
+    _installMethod(prototype, methodName, method) {
+        if (methodName.startsWith('vfunc_')) {
+            const giPrototype = prototype[Gi.gobject_prototype_symbol];
+            giPrototype[Gi.hook_up_vfunc_symbol](methodName.slice(6), method);
+        } else {
+            prototype[methodName] = method;
+        }
+    }
 }
index 6de5094414b2ea2fe8c7e54e31ffe98c83f1090c..e727f35e97400737185e8c4f5ce68030a66682fe 100644 (file)
@@ -1,5 +1,7 @@
 const JsUnit = imports.jsUnit;
 
+import GObject from 'gi://GObject';
+
 import '../../js/ui/environment.js';
 
 import {InjectionManager} from '../../js/extensions/extension.js';
@@ -16,6 +18,23 @@ class Object1 {
     }
 }
 
+class GObject1 extends GObject.Object {
+    static [GObject.properties] = {
+        'plonked': GObject.ParamSpec.boolean(
+            'plonked', '', '',
+            GObject.ParamFlags.READWRITE,
+            false),
+    };
+
+    static {
+        GObject.registerClass(this);
+    }
+
+    plonk() {
+        this.set_property('plonked', true);
+    }
+}
+
 /**
  * @param {object} object to modify
  */
@@ -91,3 +110,28 @@ JsUnit.assertEquals(obj.getNumber(), 42);
 JsUnit.assertEquals(obj.getCount(), obj.count);
 JsUnit.assert(obj.count > 0);
 JsUnit.assertRaises(() => obj.getOtherNumber());
+
+// GObject injections
+const gobj = new GObject1();
+let vfuncCalled;
+
+injectionManager.overrideMethod(
+    GObject1.prototype, 'vfunc_set_property', originalMethod => {
+        return function (...args) {
+            // eslint-disable-next-line no-invalid-this
+            originalMethod.apply(this, args);
+            vfuncCalled = true;
+        };
+    });
+
+// gobj is now modified
+vfuncCalled = false;
+gobj.plonk();
+JsUnit.assertTrue(vfuncCalled);
+
+injectionManager.clear();
+
+// gobj is unmodified again
+vfuncCalled = false;
+gobj.plonk();
+JsUnit.assertFalse(vfuncCalled);